package com.java.test.springbootminio.utils;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.io.file.FileNameUtil;
import com.java.test.springbootminio.config.MinioProperties;
import com.java.test.springbootminio.exception.FileNameNullException;
import com.java.test.springbootminio.exception.InvalidExpiresRangeException;
import io.minio.*;
import io.minio.http.Method;
import io.minio.messages.Item;
import java.io.File;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

/**
 * Created with IntelliJ IDEA.
 * Description:
 *
 * @author lzy
 * @since 2022/10/13 下午4:14
 */
@Component
public class MinioFileUtil {
    private static final Logger LOGGER = LoggerFactory.getLogger(MinioFileUtil.class);
    private static final String DEFAULT_CONTENT_TYPE = "application/octet-stream";
    private static final String UPLOAD_FILE_ERROR = "Upload file meet error.";
    private static final int DEFAULT_EXPIRY_TIME = 7 * 24 * 3600;
    private MinioClient minioClient;
    private MinioProperties minioProperties;
    private MinioBucketUtil minioBucketUtil;

    @Autowired
    public void setMinioClient(MinioClient minioClient) {
        this.minioClient = minioClient;
    }

    @Autowired
    public void setMinioProperties(MinioProperties minioProperties) {
        this.minioProperties = minioProperties;
    }

    @Autowired
    public void setMinioBucketUtil(MinioBucketUtil minioBucketUtil) {
        this.minioBucketUtil = minioBucketUtil;
    }

    /**
     * List files in bucket list.
     *
     * @return the list
     */
    public List<Item> listFilesInBucket() {
        final Iterable<Result<Item>> results = minioClient.listObjects(ListObjectsArgs.builder().bucket(minioProperties.getBucketName()).build());
        List<Item> items = new ArrayList<>();
        results.forEach(result -> {
            try {
                items.add(result.get());
            } catch (Exception e) {
                LOGGER.error("Get file info meet error.", e);
            }
        });

        return items;
    }

    /**
     * List file names in bucket list.
     *
     * @return the list
     */
    public List<String> listFileNamesInBucket() {
        return listFilesInBucket().stream().map(Item::objectName).collect(Collectors.toList());
    }

    /**
     * Stat object stat object response.
     *
     * @param bucketName the bucket name
     * @param objectName the object name
     * @return the stat object response
     */
    public StatObjectResponse statObject(String bucketName, String objectName) {
        try {
            return minioClient.statObject(StatObjectArgs.builder().bucket(bucketName).object(objectName).build());
        } catch (Exception e) {
            LOGGER.error("Get object meta info meet error.", e);
        }

        return null;
    }

    /**
     * Stat object stat object response.
     *
     * @param ObjectName the object name
     * @return the stat object response
     */
    public StatObjectResponse statObject(String ObjectName) {
        return statObject(minioProperties.getBucketName(), ObjectName);
    }

    /**
     * Gets file preview url.
     *
     * @param fileName the file name
     * @return the file preview url
     */
    public String getFilePreviewUrl(String fileName) {
        return presignedGetObject(minioProperties.getBucketName(), fileName, DEFAULT_EXPIRY_TIME);
    }

    /**
     * 生成一个给HTTP GET请求用的presigned URL
     * 浏览器/移动端的客户端可以用这个URL进行下载，即使其所在的存储桶是私有的。
     * 这个presigned URL可以设置一个失效时间，默认值是7天
     *
     * @param bucketName the bucket name 存储桶名称
     * @param objectName the object name 存储桶里的对象
     * @param expires    the expires 失效时间(单位是秒)，默认是7天，不得大于7天
     * @return the string
     */
    public String presignedGetObject(String bucketName, String objectName, int expires) {
        final boolean exists = minioBucketUtil.bucketExists(bucketName);
        if (!exists) {
            return null;
        }

        if (expires < 1 || expires > DEFAULT_EXPIRY_TIME) {
            throw new InvalidExpiresRangeException("Expires " + expires + " is out of range 1 to " + DEFAULT_EXPIRY_TIME);
        }

        String url = null;
        try {
            url = minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
                    .bucket(bucketName)
                    .object(objectName)
                    .expiry(expires)
                    .method(Method.GET).build());
        } catch (Exception e) {
            LOGGER.error("Get presigned get url meet error.", e);
        }

        return url;
    }

    /**
     * 生成一个给HTTP PUT请求用的presigned URL
     * 浏览器/移动端的客户端可以用这个URL进行上传，即使其所在的存储桶是私有的。
     * 这个presigned URL可以设置一个失效时间，默认值是7天
     *
     * @param bucketName the bucket name
     * @param objectName the object name
     * @param expires    the expires
     * @return the string
     */
    public String presignedPostObject(String bucketName, String objectName, int expires) {
        final boolean exists = minioBucketUtil.bucketExists(bucketName);
        if (!exists) {
            return null;
        }

        if (expires < 1 || expires > DEFAULT_EXPIRY_TIME) {
            throw new InvalidExpiresRangeException("Expires " + expires + " is out of range 1 to " + DEFAULT_EXPIRY_TIME);
        }

        String url = null;
        try {
            url = minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
                    .bucket(bucketName)
                    .object(objectName)
                    .expiry(expires)
                    .method(Method.POST).build());
        } catch (Exception e) {
            LOGGER.error("Get presigned put url meet error.", e);
        }

        return url;
    }

    /**
     * 上传文件
     *
     * @param bucketName  the bucket name
     * @param objectName  the object name
     * @param is          the is
     * @param size        the size
     * @param contentType the content type
     * @return the boolean
     */
    public boolean uploadFile(String bucketName, String objectName, InputStream is, long size, String contentType) {
        final boolean flag = minioBucketUtil.bucketExists(bucketName);
        if (flag) {
            try {
                final PutObjectArgs args = PutObjectArgs.builder().bucket(bucketName)
                        .object(objectName).stream(is, size < 1 ? is.available() : size, -1)
                        .contentType(StringUtils.isBlank(contentType) ? DEFAULT_CONTENT_TYPE : contentType)
                        .build();
                minioClient.putObject(args);
            } catch (Exception e) {
                LOGGER.error(UPLOAD_FILE_ERROR, e);
            }
        }

        return false;
    }

    /**
     * 上传文件
     *
     * @param multipartFile the multipart file
     * @return the string
     */
    public String uploadFile(MultipartFile multipartFile) {
        final String originalFilename = multipartFile.getOriginalFilename();
        if (StringUtils.isBlank(originalFilename)) {
            throw new FileNameNullException();
        }

        final String fileName = originalFilename.substring(originalFilename.lastIndexOf("."));
        boolean flag = false;
        try {
            flag = uploadFile(minioProperties.getBucketName(), fileName, multipartFile.getInputStream(), multipartFile.getSize(), multipartFile.getContentType());
        } catch (Exception e) {
            LOGGER.error(UPLOAD_FILE_ERROR, e);
        }

        return flag ? StringUtils.EMPTY : fileName;
    }

    /**
     * 上传文件
     *
     * @param file the file
     * @return the string
     */
    public String uploadFile(File file) {
        final String fileName = FileNameUtil.getName(file);
        if (StringUtils.isBlank(fileName)) {
            throw new FileNameNullException();
        }
        boolean flag = false;
        try {
            flag = uploadFile(minioProperties.getBucketName(), fileName, FileUtil.getInputStream(file), 0, null);
        } catch (Exception e) {
            LOGGER.error(UPLOAD_FILE_ERROR, e);
        }

        return flag ? StringUtils.EMPTY : fileName;
    }

    /**
     * Download file.
     *
     * @param fileName the file name
     * @param response the response
     */
    public void downloadFile(String fileName, HttpServletResponse response) {
        final GetObjectArgs args = GetObjectArgs.builder().bucket(minioProperties.getBucketName())
                .object(fileName).build();
        try (final GetObjectResponse res = minioClient.getObject(args)) {
            try (ServletOutputStream stream = response.getOutputStream()) {
                IoUtil.copy(res, stream);
                response.setCharacterEncoding("utf-8");
                response.addHeader("Content-Disposition", "attachment;fileName=" + fileName);
                stream.flush();
            }
        } catch (Exception e) {
            LOGGER.error("Download file meet error.", e);
        }
    }

    /**
     * Remove boolean.
     *
     * @param fileName the file name
     * @return the boolean
     */
    public boolean remove(String fileName) {
        try {
            minioClient.removeObject(RemoveObjectArgs.builder().bucket(minioProperties.getBucketName()).object(fileName).build());
        } catch (Exception e) {
            LOGGER.error("Remove file meet error.", e);
            return false;
        }

        return true;
    }
}
